﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2010
// admin@franknagl.de
//
// Parts of source code of this class from project 3dsurreal
// http://code.google.com/p/3dsurreal/
//
namespace SBIP
{
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;

    /// <summary>
    /// Simple Render-To-Texturer.
    /// </summary>
    public class RTT
    {
        private readonly GraphicsDevice graphics;
        //private DepthStencilBuffer dsb;
        //private DepthStencilBuffer backup;
        private VertexPositionTexture[] quadVex;
        private short[] quadIdx;
        private Vector2 delta;
        private RenderTarget2D target1;

        /// <summary>
        /// Create a new instance
        /// </summary>
        /// <param name="graphics">The XNA graphics device to use for 
        /// render-to-texture process.</param>
        public RTT(GraphicsDevice graphics)
        {
            this.graphics = graphics;

            InitQuad();
        }

        private void InitQuad()
        {
            //init quad
            quadVex = new VertexPositionTexture[4];
            quadVex[0] = new VertexPositionTexture(-Vector3.UnitX - Vector3.UnitY, Vector2.UnitY);
            quadVex[1] = new VertexPositionTexture(Vector3.UnitX - Vector3.UnitY, Vector2.One);
            quadVex[2] = new VertexPositionTexture(Vector3.UnitX + Vector3.UnitY, Vector2.UnitX);
            quadVex[3] = new VertexPositionTexture(-Vector3.UnitX + Vector3.UnitY, Vector2.Zero);

            quadIdx = new short[] { 0, 3, 1, 2 };

            //F quadVDec = new VertexDeclaration(graphics, VertexPositionTexture.VertexElements);
        }


        ///// <summary>
        ///// Set specified Rendertarget as RenderTarget 0. Also Set Depthbuffer
        ///// </summary>
        //private void SetTarget()
        //{
        //    graphics.SetRenderTarget(target1);
        //    //F backup = graphics.DepthStencilBuffer;
        //    //F graphics.DepthStencilBuffer = dsb;
        //    graphics.Clear(Color.Black);
        //}

        ///// <summary>
        ///// Unset Rendertarget and return the Texture
        ///// </summary>
        ///// <returns>The result texture.</returns>
        //private Texture2D UnsetTarget()
        //{
        //    RenderTarget2D target = (RenderTarget2D)graphics.GetRenderTarget(0);
        //    graphics.SetRenderTarget(0, null);
        //    graphics.DepthStencilBuffer = backup;
        //    return target.GetTexture();
        //}

        /// <summary>
        /// Draw a textured screen aligned quad, shader _must_ have parametrers "imageTexture" and "Delta"
        /// </summary>
        /// <param name="fx">Technique</param>
        /// <param name="technique">Name of current technique to be selected</param>
        /// <param name="tex">The texture that is shown on the quad</param>
        private void DrawTexturedScreenQuad(Effect fx, string technique, Texture2D tex)
        {
            //F graphics.VertexDeclaration = quadVDec;

            fx.CurrentTechnique = fx.Techniques[technique];
            fx.Parameters["ImageTexture"].SetValue(tex);
            fx.Parameters["Delta"].SetValue(delta);
            fx.Parameters["Adjustment"].SetValue(SBIP.Filter.BaseFilter.Adjustment);

            foreach (EffectPass pass in fx.CurrentTechnique.Passes)
            {
                pass.Apply();
                graphics.DrawUserIndexedPrimitives(
                    PrimitiveType.TriangleStrip,
                    quadVex, 0, 4,
                    quadIdx, 0, 2);               
            }
        }       

        /// <summary>
        /// Prepare RTT - generate targets and buffers with supplied format and texture's dimension.
        /// </summary>
        /// <param name="texture">A texture with width and height.</param>
        /// <param name="format">The format of the RenderTargets.</param>
        public void PrepareRTT(Texture2D texture, SurfaceFormat format)
        {
            //if (texture == null)
            //    int width = graphics.PresentationParameters.BackBufferWidth, 
            //                height = graphics.PresentationParameters.BackBufferHeight;
            int width = texture.Width, height = texture.Height;

            if (target1 != null)
            {
                target1.Dispose();
            }
            target1 = new RenderTarget2D(graphics, width, height, false, format, DepthFormat.Depth24Stencil8);
            //F dsb = new DepthStencilBuffer(graphics, width, height, DepthFormat.Depth24);
            delta = new Vector2(1.0f / width, 1.0f / height);
        }

        /// <summary>
        /// Applies a filter.
        /// </summary>
        /// <param name="tex">input texture</param>
        /// <param name="effect">effect to be used</param>
        /// <param name="technique">the technique name to be used</param>
        /// <param name="prepare">If set calls PrepareRTT</param>
        /// <returns>The result texture.</returns>
        public Texture2D Filter(Texture2D tex, Effect effect, string technique, bool prepare)
        {
            if (prepare)
                PrepareRTT(tex, SurfaceFormat.Color);
            
            graphics.SetRenderTarget(target1);
            //F backup = graphics.DepthStencilBuffer;
            //F graphics.DepthStencilBuffer = dsb;
            graphics.Clear(Color.Black);

            DrawTexturedScreenQuad(effect, technique, tex);

            graphics.SetRenderTarget(null);
            //graphics.DepthStencilBuffer = backup;
            return target1;
        }
    }
}
